package com.wanxin.wb.module.test;



import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Enumeration;

import javax.crypto.Cipher;

import org.apache.log4j.Logger;
import org.springframework.util.Base64Utils;
/**
 * 签名加签，验签
 * @author jiangc
 *
 */
public class Cryptor {
	private final static Logger logger = Logger.getLogger(Cryptor.class);
	public static String CHARSET="UTF-8";//编码格式
	public static String ALG="SHA256WithRSA";//签名算法
	public static String TRANSFMT = "RSA/ECB/PKCS1Padding";
    public static final String X509 = "X.509";
	 /**
     * 最大文件加密块
     */
    private static final int MAX_ENCRYPT_BLOCK = 117;
   
    /**
     * 最大文件解密块
     */
    private static final int MAX_DECRYPT_BLOCK = 128;

	
	
	 /**
     * <p>
     * 私钥加密
     * </p>
     *
     * @param data 源数据
     * @param keyStorePath 密钥库存储路径
     * @param alias 密钥库别名
     * @param password 密钥库密码
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath, String alias, String password)
            throws Exception {
        // 取得私钥
        PrivateKey privateKey = getPrivateKey(keyStorePath , password);
        /*Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }*/
        Cipher cipher2 = Cipher.getInstance(privateKey.getAlgorithm());
        cipher2.init(Cipher.ENCRYPT_MODE, privateKey);
         
        return cipher2.doFinal(data);
    }
	
    
    /**
     * <p>
     * 私钥解密
     * </p>
     *
     * @param encryptedData 已加密数据
     * @param keyStorePath 密钥库存储路径
     * @param alias 密钥库别名
     * @param password 密钥库密码
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] encryptedData, String keyStorePath , String password)
            throws Exception {
        // 取得私钥
        PrivateKey privateKey = getPrivateKey(keyStorePath , password);
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        // 解密byte数组最大长度限制: 128
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();
        return decryptedData;
    }
    
    
    /**
     * <p>
     * 公钥加密
     * </p>
     *
     * @param data 源数据
     * @param certificatePath 证书存储路径
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data, String certificatePath)
            throws Exception {
        // 取得公钥
        PublicKey publicKey = getPublicKey(certificatePath);
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        int inputLen = data.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段加密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(data, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_ENCRYPT_BLOCK;
        }
        byte[] encryptedData = out.toByteArray();
        out.close();
        return encryptedData;
    }

    
    /**
     * <p>
     * 公钥解密
     * </p>
     *
     * @param encryptedData 已加密数据
     * @param certificatePath 证书存储路径
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPublicKey(byte[] encryptedData, String certificatePath)
            throws Exception {
        PublicKey publicKey = getPublicKey(certificatePath);
        /*Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        int inputLen = encryptedData.length;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
            } else {
                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * MAX_DECRYPT_BLOCK;
        }
        byte[] decryptedData = out.toByteArray();
        out.close();*/
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        
        return cipher.doFinal(encryptedData);
    }
    
    /**
     * <p>
     * 根据证书获得公钥
     * </p>
     *
     * @param certificatePath 证书存储路径
     * @return
     * @throws Exception
     */
    static PublicKey getPublicKey(String certificatePath)
            throws Exception {
        Certificate certificate = getCertificate(certificatePath);
        PublicKey publicKey = certificate.getPublicKey();
        return publicKey;
    }
    
    /**
     * <p>
     * 获得证书
     * </p>
     *
     * @param certificatePath 证书存储路径
     * @return
     * @throws Exception
     */
    private static Certificate getCertificate(String certificatePath)
            throws Exception {
        CertificateFactory certificateFactory = CertificateFactory.getInstance(X509);
        FileInputStream in = new FileInputStream(certificatePath);
        Certificate certificate = certificateFactory.generateCertificate(in);
        in.close();
        return certificate;
    }

    
	///////////////// 外部调用api函数入口 begin ////////////////////////////////////////
    
	/**
	 * 签名函数入口
	 * message:  消息明文
	 * keyFile:  私钥文件，自己的证书文件，一般为.pfx结尾
	 * password: 私钥文件密码 
	 * @throws UnsupportedEncodingException 
	 */
	public static String sign(String message, String keyFile, String password) 
	throws InvalidKeyException, SignatureException, UnsupportedEncodingException {
		try { 
			PrivateKey key = getPrivateKey(keyFile, password); 
			//String alg = "MD5withRSA";
			byte[] msg = string2Bytes(message);
			byte[] out = sign(ALG, key, msg); 
			String base64Str = "";
			try {
				
				base64Str = Base64Utils.encodeToString(out);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("base64Str====>"+base64Str);
			
			return base64Str ;
		}
		catch(NoSuchAlgorithmException e) {
			logger.error("",e);
			return null;
		}
	}
	
	/**
	 * 签名  --返回byte[]
	 * @param message 加签内容
	 * @param keyFile 私钥地址
	 * @param password私钥密码
	 * @param alg签名算法
	 * @return
	 * @throws InvalidKeyException
	 * @throws SignatureException
	 * @throws UnsupportedEncodingException
	 */
	public static byte[] signByte(String message, String keyFile, String password) 
	throws InvalidKeyException, SignatureException, UnsupportedEncodingException {
		try { 
			PrivateKey key = getPrivateKey(keyFile, password); 
			byte[] msg = string2Bytes(message);
			logger.info("alg="+ALG);
			byte[] out = sign(ALG, key, msg); 
			return out;
		}
		catch(NoSuchAlgorithmException e) {
			logger.error("",e);
			return null;
		}
	}
	
	/** 
     * 验证签名函数入口
	 * certFile: 证书文件，对方的公钥证书文件，一般为.cer结尾
	 * message:  消息明文
	 * signature: 需要验证的消息数字签名
	 * @throws Exception 
	*/
	public static boolean verify(String certFile, String message, String signature) 
	throws Exception {
		//String alg = "MD5withRSA";
		Certificate cert = createCertificate(certFile);
		byte[] msg = string2Bytes(message);
		return verify(ALG, cert, msg, Base64Utils.decodeFromString(signature) );
	}
	
	public static boolean verifyByte(String certFile, String message, byte[] signature) 
	throws CertificateException, UnsupportedEncodingException {
		Certificate cert = createCertificate(certFile);
		byte[] msg = string2Bytes(message);
		logger.info("alg="+ALG);
		return verify(ALG, cert, msg, (signature));
	}
 
	////////////////// 外部调用api函数入口 end //////////////////////////////////////////
	
	//MD5算法
    
    
	/*
	 * 签名
	 * alg: 签名算法
	 * pk: 私钥对象
	 * message: 明文
	 */
	private static byte[] sign(String alg, PrivateKey pk, byte[] message) 
	throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
		Signature s = Signature.getInstance(alg);
		s.initSign(pk);
		s.update(message);
		
		return s.sign();
	}

	/*
	 * 根据证书文件返回证书对象
	 */
	private static Certificate createCertificate(String certfile) 
	throws CertificateException 
	{
		try {
			InputStream in = new FileInputStream(certfile);
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			X509Certificate cert = (X509Certificate) cf.generateCertificate(in);
			try {in.close();}catch(IOException e) {}
			cert.checkValidity();
			return cert;
		}
		catch(FileNotFoundException e) {
			throw new CertificateException("证书文件["+certfile+"]不存在");
		}
	}

	/**
	* 验证签名
	* alg: 算法
	* cert: 证书对象
	* message: 明文
	* signature: 签名
	*/
	private static boolean verify(String alg, Certificate cert, 
			byte[] message, byte[] signature) {
		try {
			Signature s = Signature.getInstance(alg);
			s.initVerify(cert);
			s.update(message);
			return s.verify(signature);
		}catch(Exception e) {
			e.printStackTrace();
			return false;
		}
	}
	
	/**
	 * 返回私钥
	 * file: 私钥文件(JKS格式)
	 * pass: 私钥文件密码 
	 * @throws SignatureException 
	 */
	public static PrivateKey getPrivateKey(String file, String pass) throws SignatureException { 
	    
	    try { 
		    
			    String alias = "";
				char[] password = pass.toCharArray(); 
				KeyStore ks = KeyStore.getInstance("PKCS12"); 
				ks.load(new FileInputStream(file), password);
				for (Enumeration<String> e = ks.aliases(); e.hasMoreElements();)
				{
				    alias = (String)e.nextElement(); 
					break;
				}
				privateKey = (PrivateKey) ks.getKey(alias, password);  
		    
		    return privateKey;
		}catch(FileNotFoundException e) {
			throw new SignatureException("证书文件["+file+"]不存在");
		}
		catch(Exception e) {
		    logger.error("",e);
			return null;
		}
	}
	
	private static PrivateKey privateKey = null;

	public static byte[] string2Bytes(String message) throws UnsupportedEncodingException {
		logger.info("charset="+CHARSET);
		return message.getBytes(CHARSET);
	}

	private final static byte[] hex = "0123456789ABCDEF".getBytes();
	private static int parse(char c) {
		if (c >= 'a') {
			return (c - 'a' + 10) & 0x0f;
		}
		if (c >= 'A') {
			return (c - 'A' + 10) & 0x0f;
		}
		return (c - '0') & 0x0f;
	}

	private static byte[] hexStringToBytes(String hexstr) {
		byte[] b = new byte[hexstr.length() / 2];
		int j = 0;
		for (int i = 0; i < b.length; i++) {
			char c0 = hexstr.charAt(j++);
			char c1 = hexstr.charAt(j++);
			b[i] = (byte) ( (parse(c0) << 4) | parse(c1));
		}
		return b;
	}
	
	private static String bytesToHexString(byte[] b) {
		byte[] buff = new byte[2 * b.length];
		for (int i = 0; i < b.length; i++) {
			buff[2 * i] = hex[ (b[i] >> 4) & 0x0f];
			buff[2 * i + 1] = hex[b[i] & 0x0f];
		}
		return new String(buff);
	}
	
	
	
	public static String signToBase64(String data ,String keyStorePath ,String  password ) throws Exception{
		byte[] endata = signByte(data, keyStorePath, password);
		return Base64Utils.encodeToString(endata);
		
	}
 
}
